#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include "huffman.h"
#include <time.h>

#define TAMBUF BUFSIZ*128

longComprimido=0;


typedef struct elemento {
	int offset;
	long long contexto;
	int peso;
} tipoElemento;

int longMatch(int i,int j,unsigned char buffer[],int tamanio) {
	int longitud=0;
	while (buffer[i+longitud]==buffer[j+longitud] && i+longitud < tamanio)
		longitud++;
	return longitud;
}

int main(int argc,char *argv[]) {
	clock_t t_ini=clock();

	int fp=open(argv[1],O_RDONLY);
	unsigned char buffer[TAMBUF];
	static tipoElemento tablaHash[6][4194304];
	int h[6];
	unsigned long long c[6];
	int i,ord;
	int l;
	int cantMatcheos=0;
	int colisiones[6];
	int nroMatcheos[6];
	int totalLongMatch=0;
	int nroActualizaciones=0;

	for (ord=0;ord<6;ord++) {
		colisiones[ord]=0;
		nroMatcheos[ord]=0;
	}


	resetHuffman(arboles);


	int tamanio=read(fp,buffer,TAMBUF);

	int contextoValido;

	for (i=6;i<tamanio;i++) {
		for (ord=0;ord<6 ;ord++) {
			c[ord]= buffer[i - ord -1];
			if (ord>0)
				c[ord]=(c[ord]<<(ord*8)) + c[ord-1];
		}

		h[5]=((buffer[i-6]<<14)^(buffer[i-5]<<11)^(buffer[i-4]<<9)^(buffer[i-3]<<7)^(buffer[i-2]<<5)^buffer[i-1])& 0xFFFFFF;
		h[4]=((buffer[i-5]<<14)^(buffer[i-4]<<11)^(buffer[i-3]<<7)^(buffer[i-2]<<5)^buffer[i-1])& 0xFFFFFF;
		h[3]=((buffer[i-4]<<14)^(buffer[i-3]<<9)^(buffer[i-2]<<5)^buffer[i-1])& 0xFFFFFF;
		h[2]=((buffer[i-3]<<14)^(buffer[i-2]<<7)^buffer[i-1])& 0xFFFFFF;
		h[1]=c[1];
		h[0]=c[0];

		l=0;
		contextoValido=-1;
		for (ord=5;ord>=0 ;ord--) {

			if (tablaHash[ord][h[ord]].offset!=0) { // primera pos no vacia
				while (tablaHash[ord][h[ord]].contexto!=c[ord] && tablaHash[ord][h[ord]].offset!=0) {
					h[ord]=(h[ord]+1)%4194304;
					colisiones[ord]++;
				}
				if ( tablaHash[ord][h[ord]].offset!=0 ) { //matcheo
					nroMatcheos[ord]++;
					if (contextoValido==-1) {
						l=longMatch(i,tablaHash[ord][h[ord]].offset,buffer,tamanio);
						contextoValido=ord;
						if (l>=tablaHash[ord][h[ord]].peso) {
							tablaHash[ord][h[ord]].peso=l+1;
							tablaHash[ord][h[ord]].offset=i;
						}
					} else {
						if (longMatch(i,tablaHash[ord][h[ord]].offset,buffer,tamanio)>0) tablaHash[ord][h[ord]].peso++;
					}
				} else { //inicializa en posicion distinta a la primera
					tablaHash[ord][h[ord]].offset=i;
					tablaHash[ord][h[ord]].contexto=c[ord];
				}

				tablaHash[ord][h[ord]].peso--;
				if (argc>2) {
					tablaHash[ord][h[ord]].peso=0;
				}
				if (tablaHash[ord][h[ord]].peso<=0) {
					tablaHash[ord][h[ord]].offset=i;
					//			contexto5[h5]=c5;
				}

			} else { // inicializa primera posicion
				tablaHash[ord][h[ord]].contexto=c[ord];
				tablaHash[ord][h[ord]].offset=i;
			}
		}

		if (l>0) cantMatcheos++;

		i+=l;
//		printf ("%i  ",i);
//		printf ("cont: %i long: %i   ",contextoValido,l);
		if (contextoValido!=-1)
			emitirHuffman(256+contextoValido,l);
//		printf ("\n");

		nroActualizaciones++;


//		printf ("CONT: %i  COD: %i   ", buffer[i-1],buffer[i]);
		emitirHuffman(buffer[i-1],buffer[i]);
//		printf ("\n");

		totalLongMatch+=l;
	}

	printf("Tamanio: %i\n\n",tamanio);

	printf("Nro. Matcheos: %i\n",cantMatcheos);
	printf("Long Total matcheos: %i    %f%% \n\n",totalLongMatch,((float)totalLongMatch)/((float)tamanio)*100);
	printf("Nro. Actualizaciones: %i\n\n",nroActualizaciones);

	int usados;

	for (ord=5;ord>=0;ord--) {
		usados=0;
		for (i=0;i<4194304;i++)
			if (tablaHash[ord][i].offset!=0)
				usados++;
		printf("Nro. Context. %i usados: %i\n",ord+1,usados);
		printf("Nro. Colisiones %i: %i\n",ord+1,colisiones[ord]);
		printf("Nro. Matcheos %i: %i\n",ord+1,nroMatcheos[ord]);
	}

	printf("Comprimido: %i\n",longComprimido/8);

	printf("%.16g segundos\n", (double)(clock() - t_ini) / CLOCKS_PER_SEC );

	close(fp);
	return 0;
}

